﻿using System;
using System.Collections;
using System.Globalization;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using Castle.Components.Common.TemplateEngine;
using CsvSerializer;
using CsvSerializer.CsvReader;
using CsvSerializer.CsvWriter;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using ProtoBuf;
using Formatting = Newtonsoft.Json.Formatting;

namespace mst_regex
{
    [ProtoContract, JsonObject(MemberSerialization.OptIn)]
    public class StockPosition : IStockPosition, IPersistable
    {
        [JsonProperty, ProtoMember((int) ColumnSerializationIndices.Close),
         CsvField(FieldIndex = (int) ColumnSerializationIndices.Close)]
        public decimal Close { get; set; }

        [JsonProperty, ProtoMember((int) ColumnSerializationIndices.High),
         CsvField(FieldIndex = (int) ColumnSerializationIndices.High)]
        public decimal High { get; set; }

        [JsonProperty, ProtoMember((int) ColumnSerializationIndices.Low),
         CsvField(FieldIndex = (int) ColumnSerializationIndices.Low)]
        public decimal Low { get; set; }

        [JsonProperty, ProtoMember((int) ColumnSerializationIndices.Open),
         CsvField(FieldIndex = (int) ColumnSerializationIndices.Open)]
        public decimal Open { get; set; }

        [JsonProperty, ProtoMember((int) ColumnSerializationIndices.Change),
         CsvField(FieldIndex = (int) ColumnSerializationIndices.Change)]
        public decimal Change { get; set; }

        [JsonProperty(PropertyName = "Value"), XmlElement(ElementName = "Value"),
         ProtoMember((int) ColumnSerializationIndices.Value, Name = "Value"),
         CsvField(Ignore = true)]
        public decimal ValueInMillions { get; set; }

        [JsonProperty, ProtoMember((int) ColumnSerializationIndices.Trades),
         CsvField(FieldIndex = (int) ColumnSerializationIndices.Trades)]
        public int Trades { get; set; }

        [JsonProperty, ProtoMember((int) ColumnSerializationIndices.Volume),
         CsvField(FieldIndex = (int) ColumnSerializationIndices.Volume)]
        public int Volume { get; set; }

        #region IPersistable Members

        public string ToCsv()
        {
            using (var memoryStream = new MemoryStream())
            using (var streamWriter = new StreamWriter(memoryStream))
            using (var streamReader = new StreamReader(memoryStream))
            {
                var options = new CsvWriterOptions
                                  {
                                      Delimiter = GlobalOptions.Instance.CsvDelimiterChar,
                                      HasHeaderRecord = GlobalOptions.Instance.CsvHasHeader,
                                      Quote = GlobalOptions.Instance.CsvQuoteChar
                                  };
                using (var writer = new CsvWriter(streamWriter, options))
                {
                    writer.WriteRecord(this);
                    memoryStream.Position = 0;
                    return streamReader.ReadToEnd();
                }
            }
        }

        public string ToXml()
        {
            string xmlString = null;
            try
            {
                using (var stream = new MemoryStream())
                {
                    var settings = new XmlWriterSettings
                                       {
                                           Indent = GlobalOptions.Instance.IndentSerializedOutput,
                                           OmitXmlDeclaration = true,
                                           Encoding = Encoding.UTF8,
                                           ConformanceLevel = ConformanceLevel.Auto
                                       };

                    using (XmlWriter writer = XmlWriter.Create(stream, settings))
                    {
                        var serializer = new XmlSerializer(typeof (StockPosition));
                        serializer.Serialize(writer, this);
                        writer.Flush();
                    }
                    xmlString = Encoding.Default.GetString(stream.ToArray());
                    //stream.Seek(0, SeekOrigin.Begin);
                    //xmlString = new StreamReader(stream).ReadToEnd();
                }
            }
            catch
            {
            }

            return xmlString;
        }

        public string ToJson()
        {
            return JsonConvert.SerializeObject(this, GlobalOptions.Instance.IndentSerializedOutput ? Formatting.Indented : Formatting.None);
            /*string json = null;
            try
            {
                using (var stream = new MemoryStream())
                {
                    var serializer = new DataContractJsonSerializer(typeof (StockPosition));
                    serializer.WriteObject(stream, this);
                    json = Encoding.Default.GetString(stream.ToArray());
                    //stream.Seek(0, SeekOrigin.Begin);
                    //json = new StreamReader(stream).ReadToEnd();
                }
            }
            catch
            {
            }

            return json;*/
        }

        public byte[] ToProtobuf()
        {
            byte[] proto;
            using (var stream = new MemoryStream())
            {
                Serializer.Serialize(stream, this);
                proto = stream.ToArray();
            }

            return proto;
        }

        IPersistable IPersistable.FromCsv(string csv, bool containsHeader)
        {
            return FromCsv(csv, containsHeader);
        }

        IPersistable IPersistable.FromXml(string xml)
        {
            return FromXml(xml);
        }

        IPersistable IPersistable.FromJson(string json)
        {
            return FromJson(json);
        }

        IPersistable IPersistable.FromProtobuf(byte[] proto)
        {
            return FromProtobuf(proto);
        }

        #endregion

        #region IStockPosition Members

        public string ProcessTemplate(ITemplateEngine engine, string templateFile)
        {
            var context = new Hashtable
                              {
                                  {"open", Open},
                                  {"close", Close},
                                  {"high", High},
                                  {"low", Low},
                                  {"change", Change},
                                  {"value", ValueInMillions},
                                  {"trades", Trades},
                                  {"volume", Volume},
                                  {"exchange", StockExchange},
                                  {"date", EffectiveDate.ToString("yyyy-MM-dd")},
                                  {"symbol", Symbol}
                              };

            using (var writer = new StringWriter())
            {
                engine.Process(context, templateFile, writer);
                return writer.ToString();
            }
        }

        [JsonProperty, JsonConverter(typeof (JavaScriptDateTimeConverter)), XmlElement(DataType = "date"),
         ProtoMember((int) ColumnSerializationIndices.EffectiveDate),
         CsvField(FieldIndex = (int) ColumnSerializationIndices.EffectiveDate)]
        public DateTime EffectiveDate { get; set; }

        [JsonProperty, ProtoMember((int) ColumnSerializationIndices.Symbol),
         CsvField(FieldIndex = (int) ColumnSerializationIndices.Symbol)]
        public string Symbol { get; set; }

        [JsonProperty, ProtoMember((int) ColumnSerializationIndices.StockExchange), CsvField(Ignore = true)]
        public StockExchangeType StockExchange { get; set; }

        #endregion

        public static StockPosition FromCsv(string csv, bool containsHeader)
        {
            using (var reader = new CsvReader(new StringReader(csv), containsHeader))
            {
                if (reader.FieldCount == 10)
                {
                    if (reader.ReadNextRecord())
                    {
                        return new StockPosition
                                   {
                                       Symbol = reader[(int) ColumnSerializationIndices.Symbol],
                                       EffectiveDate =
                                           DateTime.ParseExact(reader[(int) ColumnSerializationIndices.EffectiveDate],
                                                               GlobalOptions.Instance.CsvDateFormat,
                                                               CultureInfo.InvariantCulture),
                                       Open = decimal.Parse(reader[(int) ColumnSerializationIndices.Open]),
                                       Close = decimal.Parse(reader[(int) ColumnSerializationIndices.Close]),
                                       Change = decimal.Parse(reader[(int) ColumnSerializationIndices.Change]),
                                       High = decimal.Parse(reader[(int) ColumnSerializationIndices.High]),
                                       Low = decimal.Parse(reader[(int) ColumnSerializationIndices.Low]),
                                       Trades = int.Parse(reader[(int) ColumnSerializationIndices.Trades]),
                                       Volume = int.Parse(reader[(int) ColumnSerializationIndices.Volume])
                                   };
                    }
                }
            }

            return null;
        }

        public override string ToString()
        {
            return
                string.Format(
                    "symbol={0},exchange={1},date={2},open={3},high={4},low={5},close={6},change={7},trades={8},volume={9},value(mn)={10}",
                    Symbol,
                    StockExchange,
                    EffectiveDate.ToString("yyyy-MM-dd"),
                    Open, High, Low, Close, Change,
                    Trades, Volume, ValueInMillions);
        }

        public static StockPosition FromXml(string xml)
        {
            StockPosition obj = null;
            using (var stringReader = new StringReader(xml))
            {
                using (var xmlReader = new XmlTextReader(stringReader))
                {
                    var serializer = new XmlSerializer(typeof (StockPosition));
                    obj = (StockPosition) serializer.Deserialize(xmlReader);
                }
            }
            return obj;
        }

        public static StockPosition FromJson(string json)
        {
            return JsonConvert.DeserializeObject<StockPosition>(json);
        }

        public static StockPosition FromProtobuf(byte[] proto)
        {
            using (var stream = new MemoryStream())
            {
                stream.Write(proto, 0, proto.Length);
                stream.Seek(0, SeekOrigin.Begin);

                return Serializer.Deserialize<StockPosition>(stream);
            }
        }

        #region Nested type: ColumnSerializationIndices

        private enum ColumnSerializationIndices
        {
            Symbol = 0,
            EffectiveDate,
            Open,
            High,
            Low,
            Close,
            Change,
            Trades,
            Volume,
            Value,
            StockExchange
        }

        #endregion
    }
}